#! /usr/bin/env python
#
# sysgen - System Generator
#
#
# Copyright (c) 2015, Wind River Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Arguments:
#   - name of MDEF file
#   - name of directory for output files (optional)

# Generates:
#   - kernel_main.c file
#   - kernel_main.h file (local copy)
#   - micro_private_types.h file (local copy)
#   - sysgen.h file

import os
import sys
import subprocess

# global variables describing system

num_kargs = 0
num_timers = 0
num_prios = 0
num_task_irqs = 0

task_list = []
event_list = []
mutex_list = []
sema_list = []
fifo_list = []
pipe_list = []
mbx_list = []
map_list = []
pool_list = []

group_dictionary = {}
group_key_list = []

# global variables used during generation of output files

do_not_edit_warning = \
    "\n\n\n/* THIS FILE IS AUTOGENERATED -- DO NOT MODIFY! */\n\n\n"

copyright = \
    "/*\n" + \
    " * Copyright (c) 2015 Wind River Systems, Inc.\n" + \
    " *\n" + \
    " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + \
    " * you may not use this file except in compliance with the License.\n" + \
    " * You may obtain a copy of the License at\n" + \
    " *\n" + \
    " *     http://www.apache.org/licenses/LICENSE-2.0\n" + \
    " *\n" + \
    " * Unless required by applicable law or agreed to in writing, software\n" + \
    " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + \
    " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + \
    " * See the License for the specific language governing permissions and\n" + \
    " * limitations under the License.\n" + \
    " */\n"

output_dir = ""


def get_output_dir():
    """ Handle optional output directory argument """

    global output_dir
    if len(sys.argv) > 2:
        output_dir = sys.argv[2]


def write_file(filename, contents):
    """ Create file using specified name and contents """

    f = open(filename, 'w')    # overwrites file if it already exists
    f.write(contents)
    f.close()


#
# ERROR HANDLING
#


def sysgen_error(msg):
    print("\n*** sysgen error: " + msg + "\n")
    sys.exit(1)


def error_arg_count(line):
    sysgen_error("invalid number of arguments on following line\n" + line)


#
# CREATE INTERNAL REPRESENTATION OF SYSTEM
#


def mdef_parse():
    """ Parse MDEF file """

    global num_kargs
    global num_timers
    global num_prios

    # read file contents in a single shot
    with open(sys.argv[1], 'r') as infile:
        data = infile.read()

    # create list of the lines, breaking at line boundaries
    my_list = data.splitlines()

    # process each line
    for line in my_list:
        words = line.split()

        if (len(words) == 0):
            continue    # ignore blank line

        if (words[0][0] == "%"):
            continue    # ignore comment line

        if (words[0] == "CONFIG"):
            if (len(words) != 4):
                error_arg_count(line)
            num_kargs = int(words[1])
            num_timers = int(words[2])
            num_prios = int(words[3])
            continue

        if (words[0] == "TASK"):
            if (len(words) != 6):
                error_arg_count(line)
            task_list.append((words[1], int(words[2]), words[3],
                              int(words[4]), words[5]))
            continue

        if (words[0] == "TASKGROUP"):
            if (len(words) != 2):
                error_arg_count(line)
            if words[1] in group_dictionary:
                continue    # ignore re-definition of a task group
            group_bitmask = 1 << len(group_dictionary)
            group_dictionary[words[1]] = group_bitmask
            group_key_list.append(words[1])
            continue

        if (words[0] == "EVENT"):
            if (len(words) != 3):
                error_arg_count(line)
            event_list.append((words[1], words[2]))
            continue

        if (words[0] == "SEMA"):
            if (len(words) != 2):
                error_arg_count(line)
            sema_list.append((words[1],))
            continue

        if (words[0] == "MUTEX"):
            if (len(words) != 2):
                error_arg_count(line)
            mutex_list.append((words[1],))
            continue

        if (words[0] == "FIFO"):
            if (len(words) != 4):
                error_arg_count(line)
            fifo_list.append((words[1], int(words[2]), int(words[3])))
            continue

        if (words[0] == "PIPE"):
            if (len(words) != 3):
                error_arg_count(line)
            pipe_list.append((words[1], int(words[2])))
            continue

        if (words[0] == "MAILBOX"):
            if (len(words) != 2):
                error_arg_count(line)
            mbx_list.append((words[1],))
            continue

        if (words[0] == "MAP"):
            if (len(words) != 4):
                error_arg_count(line)
            map_list.append((words[1], int(words[2]), int(words[3])))
            continue

        if (words[0] == "POOL"):
            if (len(words) != 5):
                error_arg_count(line)
            pool_list.append((words[1], int(words[2]), int(words[3]),
                              int(words[4])))
            continue

        sysgen_error("unrecognized keyword %s on following line\n%s" %
                     (words[0], line))


#
# GENERATE kernel_main.c FILE
#


kernel_main_c_data = ""

kernel_main_c_filename_str = \
    "/* kernel_main.c - microkernel objects */\n\n"


def kernel_main_c_out(string):
    """ Append a string to kernel_main.c """

    global kernel_main_c_data
    kernel_main_c_data += string


def kernel_main_c_header():
    """ Generate initial portion of kernel_main.c """

    kernel_main_c_out(
        kernel_main_c_filename_str +
        copyright +
        do_not_edit_warning +
        "\n" +
        "#include <sysgen.h>\n" +
        "#include <micro_private_types.h>\n" +
        "#include <kernel_main.h>\n" +
        "#include <toolchain.h>\n" +
        "#include <sections.h>\n")


def kernel_main_c_kargs():
    """ Generate command packet variables """

    # command packets

    kernel_main_c_out("\n" +
        "struct k_args _k_server_command_packets[%s] =\n" % (num_kargs) +
        "{\n" +
        "    {NULL, NULL, 0, 0, _K_SVC_UNDEFINED},\n")
    for i in range(1, num_kargs - 1):
        kernel_main_c_out(
            "    {&_k_server_command_packets[%d], " % (i - 1) +
            "NULL, 0, 0, _K_SVC_UNDEFINED},\n")
    kernel_main_c_out(
        "    {&_k_server_command_packets[%d], " % (num_kargs - 2) +
        "NULL, 0, 0, _K_SVC_UNDEFINED}\n" +
        "};\n")

    # linked list of free command packets

    kernel_main_c_out("\n" +
        "struct nano_lifo _k_server_command_packet_free = " +
        "{{NULL, &_k_server_command_packet_free.wait_q.head}, " +
        "(void *) &_k_server_command_packets[%d]};\n" % (num_kargs - 1))


def kernel_main_c_timers():
    """ Generate timer system variables """

    if (num_timers == 0):
        return

    # timer descriptors

    kernel_main_c_out("\n" +
        "struct k_timer _k_timer_blocks[%d] =\n" % (num_timers) +
        "{\n" +
        "    {NULL, NULL, 0, 0, (struct k_args *)0xffffffff},\n")
    for i in range(1, num_timers - 1):
        kernel_main_c_out(
            "    {&_k_timer_blocks[%d], " % (i - 1) +
            "NULL, 0, 0, (struct k_args *)0xffffffff},\n")
    kernel_main_c_out(
        "    {&_k_timer_blocks[%d], " % (num_timers - 2) +
        "NULL, 0, 0, (struct k_args *)0xffffffff}\n" +
        "};\n")

    # linked list of free timers

    kernel_main_c_out("\n" +
        "struct nano_lifo _k_timer_free = " +
        "{{NULL, &_k_timer_free.wait_q.head}, " +
        "(void *) &_k_timer_blocks[%d]};\n" % (num_timers - 1))


def kernel_main_c_tasks():
    """ Generate task variables """

    global num_prios

    # task stack areas

    kernel_main_c_out("\n")
    for task in task_list:
        kernel_main_c_out("char __noinit __stack __%s_stack[%d];\n" %
                          (task[0], task[3]))

    kernel_main_c_out("extern char main_task_stack[CONFIG_MAIN_STACK_SIZE];\n")

    # declare task entry points

    kernel_main_c_out("\n")
    for task in task_list:
        kernel_main_c_out("EXTERN_C void %s(void);\n" % task[2])

    # task descriptors (including one for idle task)
    #
    # compiler puts these objects into the section as if
    # it is a stack. hence the need to reverse the list.
    # this is to preseve the order defined in MDEF file.

    kernel_main_c_out("\n")
    for task in reversed(task_list):
        name = task[0]
        prio = task[1]
        entry = task[2]
        size = task[3]
        obj_name = "_k_task_obj_%s" % (name)

        stack = "__" + task[0] + "_stack"

        # create bitmask of group(s) task belongs to
        group_bitmask = 0
        group_set = task[4][1:len(task[4]) - 1]   # drop [] surrounding groups
        if (group_set != ""):
            group_list = group_set.split(',')
            for group in group_list:
                group_bitmask |= group_dictionary[group]

        # invert bitmask to convert SYS indication to non-SYS indication
        #
        # NOTE: There actually is no SYS group; instead, there is a non-SYS
        # group that all tasks belong to unless they specify the 'SYS' name.
        # This approach allows the kernel to easily suspend all non-SYS tasks
        # during debugging, while minimizing the number of task entries that
        # have to explicitly indicate their SYS/non-SYS status.
        group_bitmask ^= group_dictionary['SYS']

        kernel_main_c_out(
            "struct k_task %s " % (obj_name)+
            "__in_section(_k_task_list, public, task) =\n" +
            "    {NULL, NULL, %d, (ktask_t)&%s,\n" % (prio, obj_name) +
            "     0x00000001, %#010x,\n" % (group_bitmask) +
            "     %s, %s, %d,\n" % (entry, stack, size) +
            "     (taskabortfunction)NULL, NULL};\n" +
            "ktask_t _k_task_ptr_%s " % (name) +
            "    __in_section(_k_task_ptr, public, task) = " +
            "    (ktask_t)&%s;\n" % (obj_name))

    kernel_main_c_out(
        "struct k_task _k_task_idle " +
        "__in_section(_k_task_list, idle, task) =\n" +
        "    {NULL, NULL, %d, 0x00000000,\n" % (num_prios - 1) +
        "     0x00000000, 0x00000000,\n" +
        "     (taskstartfunction)NULL, main_task_stack,\n"
        "     CONFIG_MAIN_STACK_SIZE,\n" +
        "     (taskabortfunction)NULL, NULL};\n" +
        "ktask_t _k_task_ptr_idle " +
        "    __in_section(_k_task_ptr, idle, task) = " +
        "    (ktask_t)&_k_task_idle;\n")

    # currently scheduled task (idle task)

    kernel_main_c_out("\n" +
        "struct k_task * _k_current_task = &_k_task_idle;\n")


def kernel_main_c_priorities():
    """ Generate task scheduling variables """

    global num_prios

    total_tasks = len(task_list) + 1

    # priority queue descriptors (lowest priority queue contains idle task)

    kernel_main_c_out("\n" +
        "struct k_tqhd _k_task_priority_list[%d] =\n" % (num_prios) +
        "{\n")
    for i in range(1, num_prios):
        kernel_main_c_out(
            "    {NULL, (struct k_task *)&_k_task_priority_list[%d]},\n" %
            (i - 1))
    kernel_main_c_out(
        "    {&_k_task_idle, &_k_task_idle}\n" +
        "};\n")

    # active priority queue (idle task's queue)

    kernel_main_c_out("\n" +
        "struct k_tqhd * K_Prio = &_k_task_priority_list[%d];\n" %
        (num_prios - 1))

    # priority queue bit map (indicates which priority queues are non-empty;
    # initially only the idle task's queue has a runnable task)

    num_bit_maps = ((num_prios + 31) // 32)

    kernel_main_c_out("\n" +
        "uint32_t _k_task_priority_bitmap[%d] = {" % (num_bit_maps))
    for i in range(1, num_bit_maps):
        kernel_main_c_out("0, ")
    kernel_main_c_out("(1u << %d)};\n" % ((num_prios - 1) & 0x1f))


def kernel_main_c_events():
    """ Generate event variables """

    global num_task_irqs

    # event descriptors

    # pre-defined event for timer
    if (num_timers > 0):
        kernel_main_c_out("DEFINE_EVENT(TICK_EVENT, _k_ticker);\n")
    else:
        kernel_main_c_out("DEFINE_EVENT(TICK_EVENT, NULL);\n")

    # project-specific events
    for event in event_list:
        kernel_main_c_out("DEFINE_EVENT(%s, %s);\n" % (event[0], event[1]))

        if (event[0].startswith("_TaskIrqEvt")):
            num_task_irqs += 1

    if (num_task_irqs > 0):
        kernel_main_c_out("const kevent_t _TaskIrqEvt_objIds[] = {\n")

        for i in range(0, num_task_irqs):
            kernel_main_c_out(
                "    (kevent_t)&_k_event_obj__TaskIrqEvt%d,\n" % (i)
            )

        kernel_main_c_out("};\n")


def kernel_main_c_mutexes():
    """ Generate mutex variables """

    total_mutexes = len(mutex_list)

    if (total_mutexes == 0):
        return

    # mutex descriptors

    kernel_main_c_out("\n")
    for mutex in mutex_list:
        name = mutex[0]
        kernel_main_c_out("struct _k_mutex_struct _k_mutex_obj_%s = " % (name) +
		"__MUTEX_DEFAULT;\n")


def kernel_main_c_semas():
    """ Generate semaphore variables """

    total_semas = len(sema_list)

    if (total_semas == 0):
        return

    # semaphore descriptors

    kernel_main_c_out("\n")
    for semaphore in sema_list:
        name = semaphore[0]
        kernel_main_c_out("struct _k_sem_struct _k_sem_obj_%s = " % (name) +
            "__K_SEMAPHORE_DEFAULT;\n")


def kernel_main_c_fifos():
    """ Generate FIFO variables """

    total_fifos = len(fifo_list)

    if (total_fifos == 0):
        return

    # FIFO buffers

    kernel_main_c_out("\n")

    for fifo in fifo_list:
        kernel_main_c_out(
            "char __noinit __%s_buffer[%d];\n" % (fifo[0], fifo[1] * fifo[2]))

    # FIFO descriptors

    kernel_main_c_out("\n")
    for fifo in fifo_list:
        name = fifo[0]
        depth = fifo[1]
        width = fifo[2]
        buffer = "__" + fifo[0] + "_buffer"
        kernel_main_c_out("struct _k_fifo_struct _k_fifo_obj_%s = " % (name) +
            "__K_FIFO_DEFAULT(%d, %d, %s);\n" % (depth, width, buffer))
    kernel_main_c_out("\n")


def kernel_main_c_pipes():
    """ Generate pipe variables """

    total_pipes = len(pipe_list)

    if (total_pipes == 0):
        return

    # pipe buffers

    kernel_main_c_out("\n")

    for pipe in pipe_list:
        kernel_main_c_out(
            "char __noinit __%s_buffer[%d];\n" % (pipe[0], pipe[1]))

    # pipe descriptors

    for pipe in pipe_list:
        name = pipe[0]
        size = pipe[1]
        buffer = "__" + pipe[0] + "_buffer"
        kernel_main_c_out("struct _k_pipe_struct _k_pipe_obj_%s = " % (name) +
            "    __K_PIPE_INITIALIZER(%d, %s);\n" % (size, buffer) +
            "kpipe_t _k_pipe_ptr_%s " % (name) +
            "    __in_section(_k_pipe_ptr, public, pipe) =\n" +
            "    (kpipe_t)&_k_pipe_obj_%s;\n" % (name))


def kernel_main_c_mailboxes():
    """ Generate mailbox variables """

    total_mbxs = len(mbx_list)

    if (total_mbxs == 0):
        return

    # mailbox descriptors

    kernel_main_c_out("\n")
    for mbx in mbx_list:
        name = mbx[0]
        kernel_main_c_out("struct _k_mbox_struct _k_mbox_obj_%s = " % (name) +
            "__K_MAILBOX_DEFAULT;\n")
    kernel_main_c_out("\n")


def kernel_main_c_maps():
    """ Generate memory map variables """

    total_maps = len(map_list)

    if (total_maps == 0):
        return

    # memory map buffers

    kernel_main_c_out("\n")

    for map in map_list:
        blocks = map[1]
        block_size = map[2]
        kernel_main_c_out("char __noinit __MAP_%s_buffer[%d];\n" %
                    (map[0], blocks * block_size))

    # memory map descriptors

    for map in map_list:
        name = map[0]
        blocks = map[1]
        block_size = map[2]
        kernel_main_c_out(
            "struct _k_mem_map_struct _k_mem_map_obj_%s = " % (name) +
            "    __K_MEM_MAP_INITIALIZER(%d, %d, __MAP_%s_buffer);\n" %
                    (blocks, block_size, map[0]) +
            "kmemory_map_t _k_mem_map_ptr_%s " % (name) +
            "    __in_section(_k_mem_map_ptr, public, mem_map) =\n" +
            "    (kmemory_map_t)&_k_mem_map_obj_%s;\n" % (name))


def kernel_main_c_pools():
    """ Generate memory pool variables """

    total_pools = len(pool_list)

    # pool global variables

    kernel_main_c_out("\nint _k_mem_pool_count = %d;\n" % (total_pools))

    if (total_pools == 0):
        kernel_main_c_out("\nstruct pool_struct * _k_mem_pool_list = NULL;\n")
        return

    # start accumulating memory pool descriptor info

    pool_descriptors = "\nstruct pool_struct _k_mem_pool_list[%d] =\n{\n" % \
                        (total_pools)
    ident = 0x00010000

    for pool in pool_list:

        kernel_main_c_out("\n")

        # create local variables relating to current pool

        min_block_size = pool[1]
        max_block_size = pool[2]
        num_maximal_blocks = pool[3]
        total_memory = max_block_size * num_maximal_blocks
        buffer = "__" + pool[0] + "_buffer"
        frag_table = "fragtab_%#010x" % ident

        # determine block sizes used by pool (including actual minimum size)

        frag_size_list = [max_block_size]
        while (ident != 0):    # loop forever
            min_block_size_actual = frag_size_list[len(frag_size_list) - 1]
            min_block_size_proposed = min_block_size_actual / 4
            if (min_block_size_proposed < min_block_size):
                break
            frag_size_list.append(min_block_size_proposed)
        frag_levels = len(frag_size_list)

        # determine size of block status arrays
        # - largest size gets special handling
        # - remainder of algorithm is a complete mystery ...

        block_status_sizes = [(num_maximal_blocks + 3) / 4]
        block_status_size_to_use = num_maximal_blocks
        for index in range(1, frag_levels):
            block_status_sizes.append(block_status_size_to_use)
            block_status_size_to_use *= 4

        # generate block status areas

        for index in range(0, frag_levels):
            kernel_main_c_out(
                "struct block_stat blockstatus_%#010x_%d[%d];\n" %
                (ident, index, block_status_sizes[index]))

        # generate memory pool fragmentation descriptor

        kernel_main_c_out("\nstruct pool_block %s[%d] =\n{\n" %
                        (frag_table, frag_levels))
        for index in range(0, frag_levels):
            kernel_main_c_out("    { %d, %d, blockstatus_%#010x_%d},\n" %
                (frag_size_list[index], block_status_sizes[index],
                 ident, index))
        kernel_main_c_out("};\n")

        # generate memory pool buffer

        kernel_main_c_out("\nchar __noinit %s[%d];\n" % (buffer, total_memory))

        # append memory pool descriptor info

        pool_descriptors += "    {%d, %d, 2, %d, %d, %d, NULL, %s, %s},\n" % \
            (max_block_size, min_block_size_actual, total_memory,
            num_maximal_blocks, frag_levels, frag_table, buffer)

        ident += 1

    # generate memory pool descriptor info

    pool_descriptors += "};\n"
    kernel_main_c_out(pool_descriptors)


def kernel_main_c_node_init():
    """ Generate node initialization routine """

    kernel_main_c_out("\n" +
        "void _k_init_dynamic(void)\n{\n")
    kernel_main_c_out("    _k_pipe_init();\n")
    kernel_main_c_out("    _k_mem_map_init();\n")
    if (len(pool_list) > 0):
        kernel_main_c_out("    _k_mem_pool_init();\n")

    kernel_main_c_out("#ifdef CONFIG_DEBUG_TRACING_KERNEL_OBJECTS\n")
    # mutex object ids
    for mutex in mutex_list:
        name = mutex[0]
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct _k_mutex_struct *, " +
            "(&_k_mutex_obj_%s), _track_list_micro_mutex);\n" % (name))
    # semaphore object ids
    for semaphore in sema_list:
        name = semaphore[0]
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct _k_sem_struct *, " +
            "(&_k_sem_obj_%s), _track_list_micro_sem);\n" % (name))
    # fifo object ids
    for fifo in fifo_list:
        name = fifo[0]
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct _k_fifo_struct *, " +
            "(&_k_fifo_obj_%s), _track_list_micro_fifo);\n" % (name))
    # mailbox object ids
    for mbx in mbx_list:
        name = mbx[0]
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct _k_mbox_struct *, " +
            "(&_k_mbox_obj_%s), _track_list_micro_mbox);\n" % (name))
    # pipe object id
    for pipe in pipe_list:
        name = pipe[0];
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct _k_pipe_struct *, " +
            "(&_k_pipe_obj_%s), _track_list_micro_pipe);\n" % (name))
    # memory map object id
    for map in map_list:
        name = map[0];
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct _k_mem_map_struct *, " +
            "(&_k_mem_map_obj_%s), _track_list_micro_mem_map);\n" % (name))
    # memory pool object id
    pool_count = 0;
    total_pools = len(pool_list);
    while (pool_count < total_pools):
        kernel_main_c_out("\tDEBUG_TRACING_OBJ_INIT(struct pool_struct *, " +
            "(&(_k_mem_pool_list[%d])), _track_list_micro_mem_pool);\n" % (pool_count))
        pool_count = pool_count + 1;
    kernel_main_c_out("#endif\n")

    kernel_main_c_out("}\n")


def kernel_main_c_generate():
    """ Generate kernel_main.c file """

    global kernel_main_c_data

    kernel_main_c_header()
    kernel_main_c_kargs()
    kernel_main_c_timers()
    kernel_main_c_tasks()
    kernel_main_c_priorities()
    kernel_main_c_events()
    kernel_main_c_mutexes()
    kernel_main_c_semas()
    kernel_main_c_fifos()
    kernel_main_c_pipes()
    kernel_main_c_mailboxes()
    kernel_main_c_maps()
    kernel_main_c_pools()
    kernel_main_c_node_init()

    write_file(output_dir + 'kernel_main.c', kernel_main_c_data)


#
# GENERATE kernel_main.h FILE
#


def kernel_main_h_generate():
    """ Generate kernel_main.h file """

    global output_dir

    subprocess.check_call([
        "cp",
        "-f",
        os.environ["ZEPHYR_BASE"] +
        "/kernel/microkernel/include/kernel_main.h",
        output_dir])


#
# GENERATE micro_private_types.h FILE
#


def micro_private_types_h_generate():
    """ Generate micro_private_types.h file """

    global output_dir

    subprocess.check_call([
        "cp",
        "-f",
        os.environ["ZEPHYR_BASE"] +
        "/kernel/microkernel/include/micro_private_types.h",
        output_dir])


#
# GENERATE sysgen.h FILE
#


sysgen_h_data = ""

sysgen_h_filename_str = \
    "/* sysgen.h - system generated microkernel definitions */\n\n"

sysgen_h_include_guard = "_SYSGEN__H_"

sysgen_h_header_include_guard_str = \
    "#ifndef " + sysgen_h_include_guard + "\n" \
    "#define " + sysgen_h_include_guard + "\n\n"


def generate_sysgen_h_header():

    global sysgen_h_data
    sysgen_h_data += \
        sysgen_h_filename_str + \
        copyright + \
        do_not_edit_warning + \
        "#include <microkernel.h>\n" + \
        sysgen_h_header_include_guard_str + \
        "\n"


def generate_taskgroup_line(taskgroup, group_id):

    global sysgen_h_data
    sysgen_h_data += \
        "#define " + taskgroup + " 0x%8.8x\n" % group_id


def generate_sysgen_h_taskgroups():

    global sysgen_h_data

    for group in group_key_list:
        generate_taskgroup_line(group, group_dictionary[group])

    sysgen_h_data += "\n"


def generate_obj_id_line(name, obj_id):

    return "#define " + name + " 0x0001%4.4x\n" % obj_id


def generate_obj_id_lines(obj_types):

    data = ""
    for obj_type in obj_types:
        for obj in obj_type[0]:
            data += generate_obj_id_line(str(obj[0]), obj_type[1])
            obj_type[1] += 1
        if obj_type[1] > 0:
            data += "\n"

    return data


def generate_sysgen_h_obj_ids():

    global sysgen_h_data
    global num_task_irqs

    # mutex object ids

    sysgen_h_data += "\n"
    for mutex in mutex_list:
        name = mutex[0]
        sysgen_h_data += \
            "extern struct _k_mutex_struct _k_mutex_obj_%s;\n" % (name)
        sysgen_h_data += \
            "#define %s ((kmutex_t)&_k_mutex_obj_%s)\n\n" % (name, name)

    # semaphore object ids

    sysgen_h_data += "\n"
    for semaphore in sema_list:
        name = semaphore[0]
        sysgen_h_data += \
            "extern struct _k_sem_struct _k_sem_obj_%s;\n" % (name)
        sysgen_h_data += \
            "#define %s ((ksem_t)&_k_sem_obj_%s)\n\n" % (name, name)

    # fifo object ids

    sysgen_h_data += "\n"
    for fifo in fifo_list:
        name = fifo[0]
        sysgen_h_data += \
            "extern struct _k_fifo_struct _k_fifo_obj_%s;\n" % (name)
        sysgen_h_data += \
            "#define %s ((kfifo_t)&_k_fifo_obj_%s)\n\n" % (name, name)

    # mailbox object ids

    sysgen_h_data += "\n"
    for mbx in mbx_list:
        name = mbx[0]
        sysgen_h_data += \
            "extern struct _k_mbox_struct _k_mbox_obj_%s;\n" % (name)
        sysgen_h_data += \
            "#define %s ((kmbox_t)&_k_mbox_obj_%s)\n\n" % (name, name)

    # pipe object id

    sysgen_h_data += "\n"
    for pipe in pipe_list:
        name = pipe[0];
        sysgen_h_data += \
            "extern struct _k_pipe_struct _k_pipe_obj_%s;\n" % (name) + \
            "#define %s ((kpipe_t)&_k_pipe_obj_%s)\n\n" % (name, name)

    # memory map object id

    sysgen_h_data += "\n"
    for map in map_list:
        name = map[0];
        sysgen_h_data += \
            "extern struct _k_mem_map_struct _k_mem_map_obj_%s;\n" % (name) + \
            "#define %s ((kmemory_map_t)&_k_mem_map_obj_%s)\n" % (name, name)

    # task object id

    sysgen_h_data += "\n"
    for task in task_list:
        name = task[0];
        sysgen_h_data += \
            "extern struct k_task _k_task_obj_%s;\n" % (name) + \
            "#define %s ((ktask_t)&_k_task_obj_%s)\n" % (name, name)

    # event object id

    sysgen_h_data += "\n"
    for event in event_list:
        # no need to expose the irq task events
        if not (event[0].startswith("_TaskIrqEvt")):
            name = event[0];
            sysgen_h_data += \
                "extern const kevent_t %s;\n" % (name)

    # all other object ids

    obj_types = [
        [pool_list, 0],
    ]
    sysgen_h_data += generate_obj_id_lines(obj_types)


sysgen_h_footer_include_guard_str = \
    "\n#endif /* " + sysgen_h_include_guard + " */\n"


def generate_sysgen_h_footer():

    global sysgen_h_data
    sysgen_h_data += \
        sysgen_h_footer_include_guard_str


def sysgen_h_generate():
    """ Generate sysgen.h file """

    generate_sysgen_h_header()
    generate_sysgen_h_taskgroups()
    generate_sysgen_h_obj_ids()
    generate_sysgen_h_footer()

    write_file(output_dir + 'sysgen.h', sysgen_h_data)


#
# SYSTEM GENERATOR MAINLINE
#


mdef_parse()
get_output_dir()
kernel_main_c_generate()
kernel_main_h_generate()
micro_private_types_h_generate()
sysgen_h_generate()
